#region File Description
/// Module:             Games Programming 
/// Assignment 1:        Platformer XNA Starter Kit 
/// Add game feature:   Main Menu/In-Game Menu window feature
/// Student Name:       Ng Chee Chiat, Nelson
/// Student Number:     S10077807A
/// Class:              3T01  
#endregion

using System;
using System.IO;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Input.Touch;


namespace Platformer
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class PlatformerGame : Microsoft.Xna.Framework.Game
    {
        private int totalScore = 0;

        //Pause Check
        private bool paused = false;

        //Menu check
        private bool menuCheck = true;

        //End game check
        private bool endGame = false;

        //Menu items
        private String title = "PlatFormer!";
        private String playGame = "Play Game";
        private String exitGame = "Exit";
        private String reset = "Reset Level";
        private String resume = "Resume";
        private String gameOver = "Game Over";
        private String replayGame = "Replay";
        private String endScore = "Total Score: ";
        private String gameOverMsg = "Congratulations! You have cleared all levels!";

        private Rectangle playGameRect;
        private Rectangle exitGameRect;
        private Rectangle resumeRect;
        private Rectangle resetRect;
        private Rectangle cursorRect;

        //Cursor Attributes
        private Vector2 cursorPosition = new Vector2(0.0f, 0.0f);
        private Vector2 cursorCenter;

        // Resources for drawing.
        private GraphicsDeviceManager graphics;
        private SpriteBatch spriteBatch;

        // Global content.
        private SpriteFont hudFont;
        private SpriteFont MenuFont;
        private SpriteFont ScoreFont;

        private Texture2D winOverlay;
        private Texture2D loseOverlay;
        private Texture2D diedOverlay;
        private Texture2D mainMenu;
        private Texture2D cursor;

        // Meta-level game state.
        private int levelIndex = -1;
        private Level level;
        private bool wasContinuePressed;

        // When the time remaining is less than the warning time, it blinks on the hud
        private static readonly TimeSpan WarningTime = TimeSpan.FromSeconds(30);

        // We store our input states so that we only poll once per frame, 
        // then we use the same input state wherever needed
        private GamePadState gamePadState;
        private KeyboardState keyboardState;
        private TouchCollection touchState;
        private AccelerometerState accelerometerState;

        // The number of levels in the Levels directory of our content. We assume that
        // levels in our content are 0-based and that all numbers under this constant
        // have a level file present. This allows us to not need to check for the file
        // or handle exceptions, both of which can add unnecessary time to level loading.
        private const int numberOfLevels = 5 + 1; //Additional '1' added for gameEnd scenario

        private int score = 0;

        public PlatformerGame()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";

#if WINDOWS_PHONE
            graphics.IsFullScreen = true;
            TargetElapsedTime = TimeSpan.FromTicks(333333);
#endif

            Accelerometer.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // Load fonts
            hudFont = Content.Load<SpriteFont>("Fonts/Hud");
            MenuFont = Content.Load<SpriteFont>("Fonts/MenuFont");
            ScoreFont = Content.Load<SpriteFont>("Fonts/ScoreFont");

            // Load overlay textures
            winOverlay = Content.Load<Texture2D>("Overlays/you_win");
            loseOverlay = Content.Load<Texture2D>("Overlays/you_lose");
            diedOverlay = Content.Load<Texture2D>("Overlays/you_died");

            //Load Menu and Cursor textures
            mainMenu = Content.Load<Texture2D>("background");
            cursor = Content.Load<Texture2D>("Cursor");
            cursorCenter = new Vector2(cursor.Width / 2, cursor.Height / 2);

            //Known issue that you get exceptions if you use Media PLayer while connected to your PC
            //See http://social.msdn.microsoft.com/Forums/en/windowsphone7series/thread/c8a243d2-d360-46b1-96bd-62b1ef268c66
            //Which means its impossible to test this from VS.
            //So we have to catch the exception and throw it away
            try
            {
                MediaPlayer.IsRepeating = true;
                MediaPlayer.Play(Content.Load<Song>("Sounds/Music"));
            }
            catch { }

        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            pauseGame(gameTime);

            if (paused != true)
            {
                // Handle polling for our input and handling high-level input
                HandleInput();

                if (menuCheck == false)
                {
                    // update our level, passing down the GameTime along with all of our input states
                    level.Update(gameTime, keyboardState, gamePadState, touchState,
                                 accelerometerState, Window.CurrentOrientation);
                }
            }

            //Update cursor position
            cursorPos(gameTime);

            base.Update(gameTime);
        }

        private void HandleInput()
        {
            // get all of our input states
            keyboardState = Keyboard.GetState();
            gamePadState = GamePad.GetState(PlayerIndex.One);
            touchState = TouchPanel.GetState();
            accelerometerState = Accelerometer.GetState();

            // Exit the game when back is pressed.
            if (gamePadState.Buttons.Back == ButtonState.Pressed)
                Exit();

            bool continuePressed =
                keyboardState.IsKeyDown(Keys.Space) ||
                gamePadState.IsButtonDown(Buttons.A) ||
                touchState.AnyTouch();

            // Perform the appropriate action to advance the game and
            // to get the player back to playing.
            if (!wasContinuePressed && continuePressed)
            {
                if (!level.Player.IsAlive)
                {
                    ReloadCurrentLevel();
                }
                else if (level.TimeRemaining == TimeSpan.Zero)
                {
                    if (endGame == false)
                    {
                        if (level.ReachedExit)
                        {
                            LoadNextLevel();
                        }
                        else
                            ReloadCurrentLevel();
                    }
                    else { }
                }
            }

            wasContinuePressed = continuePressed;
        }

        private void LoadNextLevel()
        {
            // move to the next level
            levelIndex = (levelIndex + 1) % numberOfLevels;
            
            // Unloads the content for the current level before loading the next one.
            if (level != null)
            {
                totalScore += score;
                level.Dispose();
            }

            if (levelIndex < (numberOfLevels - 1))
            {
                // Load the level.
                string levelPath = string.Format("Content/Levels/{0}.txt", levelIndex);
                using (Stream fileStream = TitleContainer.OpenStream(levelPath))
                level = new Level(Services, fileStream, levelIndex);
            }
            else
            {
                //Set total score
                endScore += level.Score.ToString();
                //End game
                endGame = true;
            }
            /// Module:             Games Programming
            /// Assignment1:        Platformer XNA Starter Kit
            /// Add game feature:   Enemy features
            /// Student Name:       Teo Zhi Wei Elvin
            /// Student Number:     S10077131J
            /// Class:              3T02
            if (level != null)
            {
                level.Score = score;
            }
        }

        private void ReloadCurrentLevel()
        {
            --levelIndex;
            LoadNextLevel();
        }

        /// <summary>
        /// Draws the game from background to foreground.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);

            spriteBatch.Begin();

            if (menuCheck == true)
            {
                DrawMainMenu();
            }

            else if (endGame == true)
            {
                drawEndGameScreen();
            }

            else if (paused == true)
            {
                drawGameMenu();
            }

            else
            {
                level.Draw(gameTime, spriteBatch);
                DrawHud();
            }

            //Mouse Cursor draw
            spriteBatch.Draw(cursor, cursorPosition, null, Color.White, 0.0f, cursorCenter, 1.0f, SpriteEffects.None, 0.0f);

            spriteBatch.End();

            base.Draw(gameTime);
        }

        private void DrawHud()
        {
            Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;
            Vector2 hudLocation = new Vector2(titleSafeArea.X, titleSafeArea.Y);
            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);
            Vector2 right = new Vector2(titleSafeArea.X + titleSafeArea.Width, titleSafeArea.Y);
            // Draw time remaining. Uses modulo division to cause blinking when the
            // player is running out of time.
            string timeString = "TIME: " + level.TimeRemaining.Minutes.ToString("00") + ":" + level.TimeRemaining.Seconds.ToString("00");
            Color timeColor;
            if (level.TimeRemaining > WarningTime ||
                level.ReachedExit ||
                (int)level.TimeRemaining.TotalSeconds % 2 == 0)
            {
                timeColor = Color.Yellow;
            }
            else
            {
                timeColor = Color.Red;
            }
            DrawShadowedString(hudFont, timeString, hudLocation, timeColor);

            // Draw score
            score = level.Score;
            float timeHeight = hudFont.MeasureString(timeString).Y;
            DrawShadowedString(hudFont, "SCORE: " + level.Score.ToString(), hudLocation + new Vector2(0.0f, timeHeight * 1.2f), Color.Yellow);

            //Draw current level number
            DrawShadowedString(hudFont, "LEVEL: " + (levelIndex + 1), hudLocation + new Vector2(0.0f, timeHeight * 2.3f), Color.Yellow);

            // Determine the status overlay message to show.
            Texture2D status = null;

            //Draw Health
            DrawShadowedString(hudFont, "HEALTH: ", right - new Vector2(240.0f, 0.0f), Color.Yellow);
            DrawShadowedString(hudFont, "MANA  : ", right - new Vector2(240.0f, -25.0f), Color.Yellow);
            if (level.TimeRemaining == TimeSpan.Zero)
            {
                if (level.ReachedExit)
                {
                    status = winOverlay;
                }
                else
                {
                    status = loseOverlay;
                }
            }
            else if (!level.Player.IsAlive)
            {
                status = diedOverlay;
            }

            if (status != null)
            {
                // Draw status message.
                Vector2 statusSize = new Vector2(status.Width, status.Height);
                spriteBatch.Draw(status, center - statusSize / 2, Color.White);
            }
        }

        private void DrawShadowedString(SpriteFont font, string value, Vector2 position, Color color)
        {
            spriteBatch.DrawString(font, value, position + new Vector2(1.0f, 1.0f), Color.Black);
            spriteBatch.DrawString(font, value, position, color);
        }

        private void DrawMainMenu()
        {
            Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;
            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);
            Vector2 menuSize = new Vector2(mainMenu.Width, mainMenu.Height);

            //Drawing menu background
            spriteBatch.Draw(mainMenu, center - menuSize / 2, Color.White);

            //Drawing menu items
            //Drawing title
            Vector2 stringDimensions = ScoreFont.MeasureString(title);
            Vector2 stringPosition = new Vector2(center.X - (stringDimensions.X / 2), titleSafeArea.Y + (stringDimensions.Y / 2));
            spriteBatch.DrawString(ScoreFont, title, stringPosition, Color.Yellow);

            //Drawing "Play Game"
            stringDimensions = MenuFont.MeasureString(playGame);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), center.Y - (stringDimensions.Y / 2));

            //Bounding Rectangle for "Play"
            playGameRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)center.Y - ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (playGameRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, playGame, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, playGame, stringPosition, Color.White);
            }

            //Drawing "Exit"
            stringDimensions = MenuFont.MeasureString(exitGame);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), stringPosition.Y + stringDimensions.Y);

            //Bounding Rectangle for "Exit"
            exitGameRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)stringPosition.Y + ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (exitGameRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.White);
            }
        }

        //Cursor
        private void cursorPos(GameTime gameTime)
        {
            MouseState mouse = Mouse.GetState();

            //Get mouse position
            cursorPosition.X = mouse.X;
            cursorPosition.Y = mouse.Y;

            //Bounding Rectangle
            cursorRect = new Rectangle((int)cursorPosition.X, (int)cursorPosition.Y, 1, 1);

            if (cursorRect.Intersects(playGameRect))
            {
                if (mouse.LeftButton == ButtonState.Pressed)
                {
                    if (endGame == true)
                    {
                        //Reset level index
                        levelIndex = -1;

                        //Reinitialize scores
                        totalScore = 0;
                        score = 0;

                        //Disable end game scenario
                        endGame = false;
                    }
                    else
                    { }

                    //Menu check to false
                    menuCheck = false;
                    //Load Level
                    LoadNextLevel();

                    //Clear menu rectangles
                    playGameRect = new Rectangle(0, 0, 0, 0);
                    exitGameRect = new Rectangle(0, 0, 0, 0);
                }
            }

            else if (cursorRect.Intersects(exitGameRect))
            {
                if (mouse.LeftButton == ButtonState.Pressed)
                {
                    this.Exit();
                }
            }

            else if (cursorRect.Intersects(resumeRect))
            {
                if (mouse.LeftButton == ButtonState.Pressed)
                {
                    paused = false;

                    //Clear menu rectangles
                    resumeRect = new Rectangle(0, 0, 0, 0);
                    resetRect = new Rectangle(0, 0, 0, 0);
                    exitGameRect = new Rectangle(0, 0, 0, 0);
                }
            }

            else if (cursorRect.Intersects(resetRect))
            {
                if (mouse.LeftButton == ButtonState.Pressed)
                {
                    // Unloads the content for the current level before loading the next one.
                    if (level != null)
                        level.Dispose();

                    // Load the level.
                    string levelPath = string.Format("Content/Levels/{0}.txt", levelIndex);
                    using (Stream fileStream = TitleContainer.OpenStream(levelPath))
                        level = new Level(Services, fileStream, levelIndex);

                    //Unpause game
                    paused = false;

                    //Clear menu rectangles
                    resumeRect = new Rectangle(0, 0, 0, 0);
                    resetRect = new Rectangle(0, 0, 0, 0);
                    exitGameRect = new Rectangle(0, 0, 0, 0);
                }
            }

        }

        //Method to pause game
        private void pauseGame(GameTime gameTime)
        {
            keyboardState = Keyboard.GetState();
            //Pause the game
            if (paused == false && keyboardState.IsKeyDown(Keys.P))
            {
                paused = true;
            }
            else
            {}
        }

        //Method to draw In-Game Menu
        private void drawGameMenu()
        {
            Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;
            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);
            Vector2 menuSize = new Vector2(mainMenu.Width, mainMenu.Height);

            //Drawing menu background
            spriteBatch.Draw(mainMenu, center - menuSize / 2, Color.White);

            //Drawing menu items
            //Drawing "Resume"
            Vector2 stringDimensions = MenuFont.MeasureString(resume);
            Vector2 stringPosition = new Vector2(center.X - (stringDimensions.X / 2), center.Y - (stringDimensions.Y / 2));



            //Bounding Rectangle for "Resume"
            resumeRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)center.Y - ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (resumeRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, resume, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, resume, stringPosition, Color.White);
            }

            //Drawing "Reset Level"
            stringDimensions = MenuFont.MeasureString(reset);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), stringPosition.Y + stringDimensions.Y);

            //Bounding Rectangle for "Reset Level"
            resetRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)stringPosition.Y + ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (resetRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, reset, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, reset, stringPosition, Color.White);
            }

            //Drawing "Exit"
            stringDimensions = MenuFont.MeasureString(exitGame);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), stringPosition.Y + (stringDimensions.Y * 2));

            //Bounding Rectangle for "Exit"
            exitGameRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)stringPosition.Y + ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (exitGameRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.White);
            }
        }

        //Method to draw end game screen
        private void drawEndGameScreen()
        {
            Rectangle titleSafeArea = GraphicsDevice.Viewport.TitleSafeArea;
            Vector2 center = new Vector2(titleSafeArea.X + titleSafeArea.Width / 2.0f,
                                         titleSafeArea.Y + titleSafeArea.Height / 2.0f);
            Vector2 menuSize = new Vector2(mainMenu.Width, mainMenu.Height);

            //Drawing menu background
            spriteBatch.Draw(mainMenu, center - menuSize / 2, Color.White);

            //Drawing "Game Over"
            Vector2 stringPosition = new Vector2(titleSafeArea.X, titleSafeArea.Y);
            spriteBatch.DrawString(ScoreFont, gameOver, stringPosition, Color.Yellow);

            //Drawing game over message
            Vector2 stringDimensions = MenuFont.MeasureString(gameOver);
            stringPosition = new Vector2(titleSafeArea.X, stringPosition.Y + (stringDimensions.Y * 2));
            spriteBatch.DrawString(MenuFont, gameOverMsg, stringPosition, Color.Yellow);

            //Drawing total score
            stringPosition = new Vector2(titleSafeArea.X, stringPosition.Y + (stringDimensions.Y * 2));
            spriteBatch.DrawString(MenuFont, endScore, stringPosition, Color.Yellow);

            //Drawing "Replay Game"
            stringDimensions = MenuFont.MeasureString(replayGame);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), stringPosition.Y + (stringDimensions.Y * 2));

            //Bounding Rectangle for "Replay Game"
            playGameRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)center.Y - ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (playGameRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, replayGame, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, replayGame, stringPosition, Color.White);
            }

            //Drawing "Exit"
            stringDimensions = MenuFont.MeasureString(exitGame);
            stringPosition = new Vector2(center.X - (stringDimensions.X / 2), stringPosition.Y + stringDimensions.Y);

            //Bounding Rectangle for "Exit"
            exitGameRect = new Rectangle((int)center.X - ((int)stringDimensions.X / 2), (int)stringPosition.Y + ((int)stringDimensions.Y / 2), (int)stringDimensions.X, (int)stringDimensions.Y);

            if (exitGameRect.Intersects(cursorRect))
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.Yellow);
            }

            else
            {
                spriteBatch.DrawString(MenuFont, exitGame, stringPosition, Color.White);
            }
        }

    }
}
